package com.zwf.domain.strategy.service.armory;

import com.zwf.domain.strategy.model.entity.StrategyAwardEntity;
import com.zwf.domain.strategy.model.entity.StrategyEntity;
import com.zwf.domain.strategy.model.entity.StrategyRuleEntry;
import com.zwf.domain.strategy.repository.IStrategyRepository;
import com.zwf.types.common.Constants;
import com.zwf.types.enums.ResponseCode;
import com.zwf.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

/**
 * @Author zwf
 * @description 策略装配库，负责初始化策略计算
 * @date 2024/6/29 21:16
 */
@Slf4j
@Service
public class StrategyArmoryDispatch implements IStrategyArmory, IStrategyDispatch{

    @Autowired
    private IStrategyRepository repository;

    //装配策略库
    @Override
    public void assembleLotteryStrategy(Long strategyId){
        log.info("正在装配策略库。。。");
        //1、查询策略配置
        List<StrategyAwardEntity> strategyAwardEntityList = repository.queryStrategyAwardList(strategyId);
        if(strategyAwardEntityList == null || strategyAwardEntityList.isEmpty()){
            throw new AppException(ResponseCode.STRATEGY_AWARD_LIST_IS_NULL.getCode(),ResponseCode.STRATEGY_AWARD_LIST_IS_NULL.getInfo());
        }
        //2、将所有策略奖品进行装配
        assemble(String.valueOf(strategyId), strategyAwardEntityList);

        //3、根据权重规则进行装配
        //3.1、查询策略
        StrategyEntity strategyEntity = repository.queryStategyEntity(strategyId);
        if(strategyEntity == null) {
            throw new AppException(ResponseCode.STRATEGY_ENTITY_IS_NOLL.getCode(),ResponseCode.STRATEGY_ENTITY_IS_NOLL.getInfo());
        }
        //3.2、查询策略是否有权重规则
        if(strategyEntity.getRuleWeight()){
            StrategyRuleEntry strategyRuleEntry = repository.queryStrategyRuleEntity(strategyId,Constants.RuleModel.RULE_WEIGHT);
            //权重规则已使用但未配置
            if(strategyRuleEntry == null) {
                throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(),ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
            }
            Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntry.getRuleWeightValues();
            Set<String> keys = ruleWeightValueMap.keySet();
            for (String key : keys){
                List<Integer> values = ruleWeightValueMap.get(key);
                ArrayList<StrategyAwardEntity> strategyAwardEntityListClone = new ArrayList<>(strategyAwardEntityList);
                strategyAwardEntityListClone.removeIf(entity -> !values.contains(entity.getAwardId()));
                assemble(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntityListClone);
            }
        }
    }

    //装配
    public void assemble(String key, List<StrategyAwardEntity> strategyAwardEntityList) {
        //1、获取最小概率值
        BigDecimal minAwardRate = strategyAwardEntityList.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        //2、获取概率总和
        BigDecimal totalAwardRate = strategyAwardEntityList.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .reduce(BigDecimal.ZERO,BigDecimal::add);

        //3、将总概率按最小概率进行划分,得到概率表大小(不一定为真实概率表大小，精度截取会产生误差)
        BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);

        //4、生成策略
        ArrayList<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
        for(StrategyAwardEntity strategyAward : strategyAwardEntityList){
            Integer awardId = strategyAward.getAwardId();
            BigDecimal awardRate = strategyAward.getAwardRate();
            //计算出每个概率值需要存放到查找表的数量，循环填充
            for(int i = 0; i < rateRange.multiply(awardRate.divide(totalAwardRate,2, RoundingMode.CEILING)).setScale(0, RoundingMode.CEILING).intValue();i++){
                strategyAwardSearchRateTables.add(awardId);
            }
        }

        //5、乱序
        Collections.shuffle(strategyAwardSearchRateTables);

        //6、生成奖品概率表
        HashMap<Integer, Integer> shuffleStrategyAwardSearchRateTables = new HashMap<>();
        for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
            shuffleStrategyAwardSearchRateTables.put(i, strategyAwardSearchRateTables.get(i));
        }

        //7、存储到Redis
        repository.storeStrategyAwardSearchRateTablesToRedis(key, shuffleStrategyAwardSearchRateTables.size(), shuffleStrategyAwardSearchRateTables);

    }

    //获取抽奖策略装配的随机结果
    @Override
    public Integer getRandomAwardId(Long strategyId) {
        //分布式部署下，不一定为当前应用做的策略装配，也就是值不一定会保存到本应用，而是分布式应用，所以需要从Redis中获取
        Integer rateRange = repository.getRateRange(strategyId);
        //通过生成的随机值，从概率表中获取奖品id
        return repository.getStrategyAwardAssemble(String.valueOf(strategyId), new SecureRandom().nextInt(rateRange));
    }

    //获取权重筛选后的抽奖策略装配的随机结果
    @Override
    public Integer getRandomAwardIdByRuleWeight(Long strategyId, String ruleWeight) {
        String key = String .valueOf(strategyId).concat("_").concat(ruleWeight);
        //分布式部署下，不一定为当前应用做的策略装配，也就是值不一定会保存到本应用，而是分布式应用，所以需要从Redis中获取
        Integer rateRange = repository.getRateRange(key);
        //通过生成的随机值，从概率表中获取奖品id
        return repository.getStrategyAwardAssemble(key, new SecureRandom().nextInt(rateRange));
    }
}
